home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / python-gdata / gdata / urlfetch.py < prev   
Encoding:
Python Source  |  2008-09-05  |  9.1 KB  |  248 lines

  1. #!/usr/bin/python
  2. #
  3. # Copyright (C) 2008 Google Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. #      http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16.  
  17.  
  18. """Provides HTTP functions for gdata.service to use on Google App Engine
  19.  
  20. AppEngineHttpClient: Provides an HTTP request method which uses App Engine's
  21.    urlfetch API. Set the http_client member of a GDataService object to an
  22.    instance of an AppEngineHttpClient to allow the gdata library to run on
  23.    Google App Engine.
  24.  
  25. run_on_appengine: Function which will modify an existing GDataService object
  26.    to allow it to run on App Engine. It works by creating a new instance of
  27.    the AppEngineHttpClient and replacing the GDataService object's 
  28.    http_client.
  29.  
  30. HttpRequest: Function that wraps google.appengine.api.urlfetch.Fetch in a 
  31.     common interface which is used by gdata.service.GDataService. In other 
  32.     words, this module can be used as the gdata service request handler so 
  33.     that all HTTP requests will be performed by the hosting Google App Engine
  34.     server. 
  35. """
  36.  
  37.  
  38. __author__ = 'api.jscudder (Jeff Scudder)'
  39.  
  40.  
  41. import StringIO
  42. import atom.service
  43. import atom.http_interface
  44. from google.appengine.api import urlfetch
  45.  
  46.  
  47. def run_on_appengine(gdata_service):
  48.   """Modifies a GDataService object to allow it to run on App Engine.
  49.  
  50.   Args:
  51.     gdata_service: An instance of AtomService, GDataService, or any
  52.         of their subclasses which has an http_client member.
  53.   """
  54.   gdata_service.http_client = AppEngineHttpClient()
  55.  
  56.  
  57. class AppEngineHttpClient(atom.http_interface.GenericHttpClient):
  58.   def __init__(self, headers=None):
  59.     self.debug = False
  60.     self.headers = headers or {}
  61.  
  62.   def request(self, operation, url, data=None, headers=None):
  63.     """Performs an HTTP call to the server, supports GET, POST, PUT, and
  64.     DELETE.
  65.  
  66.     Usage example, perform and HTTP GET on http://www.google.com/:
  67.       import atom.http
  68.       client = atom.http.HttpClient()
  69.       http_response = client.request('GET', 'http://www.google.com/')
  70.  
  71.     Args:
  72.       operation: str The HTTP operation to be performed. This is usually one
  73.           of 'GET', 'POST', 'PUT', or 'DELETE'
  74.       data: filestream, list of parts, or other object which can be converted
  75.           to a string. Should be set to None when performing a GET or DELETE.
  76.           If data is a file-like object which can be read, this method will
  77.           read a chunk of 100K bytes at a time and send them.
  78.           If the data is a list of parts to be sent, each part will be
  79.           evaluated and sent.
  80.       url: The full URL to which the request should be sent. Can be a string
  81.           or atom.url.Url.
  82.       headers: dict of strings. HTTP headers which should be sent
  83.           in the request.
  84.     """
  85.     all_headers = self.headers.copy()
  86.     if headers:
  87.       all_headers.update(headers)
  88.  
  89.     # Construct the full payload.
  90.     # Assume that data is None or a string.
  91.     data_str = data
  92.     if data:
  93.       if isinstance(data, list):
  94.         # If data is a list of different objects, convert them all to strings
  95.         # and join them together.
  96.         converted_parts = [__ConvertDataPart(x) for x in data]
  97.         data_str = ''.join(converted_parts)
  98.       else:
  99.         data_str = __ConvertDataPart(data)
  100.  
  101.     # If the list of headers does not include a Content-Length, attempt to
  102.     # calculate it based on the data object.
  103.     if data and 'Content-Length' not in all_headers:
  104.       all_headers['Content-Length'] = len(data_str)
  105.  
  106.     # Set the content type to the default value if none was set.
  107.     if 'Content-Type' not in all_headers:
  108.       all_headers['Content-Type'] = 'application/atom+xml'
  109.  
  110.     # Lookup the urlfetch operation which corresponds to the desired HTTP verb.
  111.     if operation == 'GET':
  112.       method = urlfetch.GET
  113.     elif operation == 'POST':
  114.       method = urlfetch.POST
  115.     elif operation == 'PUT':
  116.       method = urlfetch.PUT
  117.     elif operation == 'DELETE':
  118.       method = urlfetch.DELETE
  119.     else:
  120.       method = None
  121.     return HttpResponse(urlfetch.Fetch(url=str(url), payload=data_str,
  122.         method=method, headers=all_headers))
  123.  
  124.  
  125. def HttpRequest(service, operation, data, uri, extra_headers=None,
  126.     url_params=None, escape_params=True, content_type='application/atom+xml'):
  127.   """Performs an HTTP call to the server, supports GET, POST, PUT, and DELETE.
  128.  
  129.   This function is deprecated, use AppEngineHttpClient.request instead.
  130.  
  131.   To use this module with gdata.service, you can set this module to be the
  132.   http_request_handler so that HTTP requests use Google App Engine's urlfetch.
  133.   import gdata.service
  134.   import gdata.urlfetch
  135.   gdata.service.http_request_handler = gdata.urlfetch
  136.  
  137.   Args:
  138.     service: atom.AtomService object which contains some of the parameters
  139.         needed to make the request. The following members are used to
  140.         construct the HTTP call: server (str), additional_headers (dict),
  141.         port (int), and ssl (bool).
  142.     operation: str The HTTP operation to be performed. This is usually one of
  143.         'GET', 'POST', 'PUT', or 'DELETE'
  144.     data: filestream, list of parts, or other object which can be
  145.         converted to a string.
  146.         Should be set to None when performing a GET or PUT.
  147.         If data is a file-like object which can be read, this method will read
  148.         a chunk of 100K bytes at a time and send them.
  149.         If the data is a list of parts to be sent, each part will be evaluated
  150.         and sent.
  151.     uri: The beginning of the URL to which the request should be sent.
  152.         Examples: '/', '/base/feeds/snippets',
  153.         '/m8/feeds/contacts/default/base'
  154.     extra_headers: dict of strings. HTTP headers which should be sent
  155.         in the request. These headers are in addition to those stored in
  156.         service.additional_headers.
  157.     url_params: dict of strings. Key value pairs to be added to the URL as
  158.         URL parameters. For example {'foo':'bar', 'test':'param'} will
  159.         become ?foo=bar&test=param.
  160.     escape_params: bool default True. If true, the keys and values in
  161.         url_params will be URL escaped when the form is constructed
  162.         (Special characters converted to %XX form.)
  163.     content_type: str The MIME type for the data being sent. Defaults to
  164.         'application/atom+xml', this is only used if data is set.
  165.   """
  166.   full_uri = atom.service.BuildUri(uri, url_params, escape_params)
  167.   (server, port, ssl, partial_uri) = atom.service.ProcessUrl(service, full_uri)
  168.   # Construct the full URL for the request.
  169.   if ssl:
  170.     full_url = 'https://%s%s' % (server, partial_uri)
  171.   else:
  172.     full_url = 'http://%s%s' % (server, partial_uri)
  173.  
  174.   # Construct the full payload. 
  175.   # Assume that data is None or a string.
  176.   data_str = data
  177.   if data:
  178.     if isinstance(data, list):
  179.       # If data is a list of different objects, convert them all to strings
  180.       # and join them together.
  181.       converted_parts = [__ConvertDataPart(x) for x in data]
  182.       data_str = ''.join(converted_parts)
  183.     else:
  184.       data_str = __ConvertDataPart(data)
  185.  
  186.   # Construct the dictionary of HTTP headers.
  187.   headers = {}
  188.   if isinstance(service.additional_headers, dict):
  189.     headers = service.additional_headers.copy()
  190.   if isinstance(extra_headers, dict):
  191.     for header, value in extra_headers.iteritems():
  192.       headers[header] = value
  193.   # Add the content type header (we don't need to calculate content length,
  194.   # since urlfetch.Fetch will calculate for us).
  195.   if content_type:
  196.     headers['Content-Type'] = content_type
  197.  
  198.   # Lookup the urlfetch operation which corresponds to the desired HTTP verb.
  199.   if operation == 'GET':
  200.     method = urlfetch.GET
  201.   elif operation == 'POST':
  202.     method = urlfetch.POST
  203.   elif operation == 'PUT':
  204.     method = urlfetch.PUT
  205.   elif operation == 'DELETE':
  206.     method = urlfetch.DELETE
  207.   else:
  208.     method = None
  209.   return HttpResponse(urlfetch.Fetch(url=full_url, payload=data_str, 
  210.       method=method, headers=headers))
  211.  
  212.  
  213. def __ConvertDataPart(data):
  214.   if not data or isinstance(data, str):
  215.     return data
  216.   elif hasattr(data, 'read'):
  217.     # data is a file like object, so read it completely.
  218.     return data.read()
  219.   # The data object was not a file.
  220.   # Try to convert to a string and send the data.
  221.   return str(data)
  222.  
  223.  
  224. class HttpResponse(object):
  225.   """Translates a urlfetch resoinse to look like an hhtplib resoinse.
  226.   
  227.   Used to allow the resoinse from HttpRequest to be usable by gdata.service
  228.   methods.
  229.   """
  230.  
  231.   def __init__(self, urlfetch_response):
  232.     self.body = StringIO.StringIO(urlfetch_response.content)
  233.     self.headers = urlfetch_response.headers
  234.     self.status = urlfetch_response.status_code
  235.     self.reason = ''
  236.  
  237.   def read(self, length=None):
  238.     if not length:
  239.       return self.body.read()
  240.     else:
  241.       return self.body.read(length)
  242.  
  243.   def getheader(self, name):
  244.     if not self.headers.has_key(name):
  245.       return self.headers[name.lower()]
  246.     return self.headers[name]
  247.     
  248.